import cv2
import sys
from PySide6.QtWidgets import *
from PySide6.QtCore import *
from PySide6.QtGui import *
# 定义用于映射的字符集
ASCII_CHARS = '@%#*+=-:. '

class VideoThread(QThread):
    frame_ready = Signal(str, int)  # 发送ASCII字符串和每行字符数

    def __init__(self):
        super().__init__()
        self.mutex = QMutex()
        self._is_running = False
        self.video_path = ""
        self.current_frame_pos = 0

    def run(self):
        self.mutex.lock()
        self._is_running = True
        self.mutex.unlock()

        cap = None
        try:
            while self.is_running:
                if not cap or not cap.isOpened():
                    if not self.video_path:
                        self.msleep(100)  # 若没有视频路径，稍作等待
                        continue
                    cap = cv2.VideoCapture(self.video_path)
                    if not cap.isOpened():
                        self.msleep(100)  # 若视频打开失败，稍作等待
                        continue
                    cap.set(cv2.CAP_PROP_POS_FRAMES, self.current_frame_pos)

                ret, frame = cap.read()
                if not ret:
                    # 视频播放结束，重置帧位置
                    self.current_frame_pos = 0
                    cap.set(cv2.CAP_PROP_POS_FRAMES, self.current_frame_pos)
                    continue

                # 处理视频帧
                resized = self.resize_frame(frame)
                gray = self.grayify(resized)
                ascii_str = self.pixels_to_ascii(gray)
                width = resized.shape[1]

                # 发送信号
                self.frame_ready.emit(ascii_str, width)
                self.msleep(8)  # 约60fps
                self.current_frame_pos = cap.get(cv2.CAP_PROP_POS_FRAMES)

        finally:
            if cap:
                cap.release()
            self.current_frame_pos = 0


    def stop(self):
        self.mutex.lock()
        self._is_running = False
        self.mutex.unlock()

    @property
    def is_running(self):
        try:
            self.mutex.lock()
            return self._is_running
        finally:
            self.mutex.unlock()

    @staticmethod
    def resize_frame(frame, new_width=100):
        height, width = frame.shape[:2]
        ratio = height / width
        new_height = int(new_width * ratio)
        return cv2.resize(frame, (new_width, new_height))

    @staticmethod
    def grayify(frame):
        return cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    @staticmethod
    def pixels_to_ascii(frame):
        pixels = frame.flatten()
        max_index = len(ASCII_CHARS) - 1
        return ''.join([ASCII_CHARS[min(pixel // 25, max_index)] for pixel in pixels])
    
class MovToAscii(QMainWindow):
    def __init__(self):
        super().__init__()

        self.video_thread = VideoThread()
        self.video_thread.frame_ready.connect(self.update_frame)
        self.current_font_size = 8
        
        self.initUI()

    def initUI(self):
        self.setWindowTitle("Video to ASCII")
        self.setGeometry(100, 100, 400, 600)

        central_widget = QWidget()
        self.setCentralWidget(central_widget)

        layout = QVBoxLayout()
        central_widget.setLayout(layout)

        self.label = QLabel()
        self.label.setFont(QFont("Courier New", self.current_font_size))
        self.label.setAlignment(Qt.AlignLeft | Qt.AlignTop)
        layout.addWidget(self.label)

        self.btn_open = QPushButton("打开视频文件")
        self.btn_open.clicked.connect(self.open_file)
        layout.addWidget(self.btn_open)

        self.btn_stop = QPushButton("停止播放")
        self.btn_stop.clicked.connect(self.stop_playback)
        layout.addWidget(self.btn_stop)

    def contextMenuEvent(self, event):
        # 创建右键菜单
        menu = QMenu(self)
        change_font_action = menu.addAction("修改字体大小")
        action = menu.exec(event.globalPos())
        if action == change_font_action:
            self.show_font_size_dialog()

    def show_font_size_dialog(self):
        # 创建滑动条对话框
        dialog = QDialog(self)
        layout = QVBoxLayout()

        slider = QSlider(Qt.Horizontal)
        slider.setRange(4, 24)
        slider.setValue(self.current_font_size)
        layout.addWidget(slider)

        confirm_button = QPushButton("确定")
        confirm_button.clicked.connect(lambda: self.change_font_size(slider.value(), dialog))
        layout.addWidget(confirm_button)

        dialog.setLayout(layout)
        dialog.exec()

    def change_font_size(self, size, dialog):
        self.current_font_size = size
        font = self.label.font()
        font.setPointSize(size)
        self.label.setFont(font)
        dialog.close()
    def open_file(self):
        path, _ = QFileDialog.getOpenFileName(
            self, "选择视频文件", "", "视频文件 (*.mp4 *.avi *.mov)"
        )
        if path:
            self.stop_playback()
            self.video_thread.video_path = path
            self.video_thread.start()

    def stop_playback(self):
        if self.video_thread.isRunning():
            self.video_thread.stop()
            self.video_thread.wait()
            self.btn_stop.setText("播放")
        else:
            if self.video_thread.video_path:
                self.video_thread.start()
                self.btn_stop.setText("停止播放")
            

    def update_frame(self, ascii_str, width):
        lines = [ascii_str[i:i+width] for i in range(0, len(ascii_str), width)]
        self.label.setText('\n'.join(lines))

    def closeEvent(self, event):
        self.stop_playback()
        super().closeEvent(event)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MovToAscii()
    window.show()
    sys.exit(app.exec())

